#include "iap_pro.h"
#include <string.h>
#include "stm32f10x.h"
#include "core_sha256.h"

/**
* @函数名:      FlashProgramStart
* @功能描述:    解锁FLASH
* @输入参数:    无
* @输出参数:    无
* @返回值:      无
*/
void FlashProgramStart(void)
{
    FLASH_Unlock();
}

/**
* @函数名:      ProgramFlashWord
* @功能描述:    把data数据烧写到address，烧写长度为len字节，len必须4字节对齐，否则烧写异常
* @输入参数:    无
* @输出参数:    无
* @返回值:      无
*/
void ProgramFlash(uint32_t address,uint8_t* data,uint32_t len)
{
    uint32_t i = 0;
    //准备写入
    
    for(i = 0;i < len;i+=4,address+=4)
    {
        FLASH_ProgramWord(address,*(uint32_t*)&data[i]);
    }
    
}

/**
* @函数名:      EraseApp1
* @功能描述:    擦除APP1
* @输入参数:    无
* @输出参数:    无
* @返回值:      无
*/
void EraseApp1(void)
{
    uint32_t address = FLASH_APP1_ADDRESS;
    for(;address < FLASH_APP1_ADDRESS + APP_MAX_SIZE;address += FLASH_PAGE_SIZE)
    {
        FLASH_ErasePage(address);
    }
}

/**
* @函数名:      EraseApp2
* @功能描述:    擦除APP2
* @输入参数:    无
* @输出参数:    无
* @返回值:      无
*/
void EraseApp2(void)
{
    uint32_t address = FLASH_APP2_ADDRESS;
    for(;address < FLASH_APP2_ADDRESS + APP_MAX_SIZE;address += FLASH_PAGE_SIZE)
    {
        FLASH_ErasePage(address);
    }
}

/**
* @函数名:      CopyToApp2
* @功能描述:    把APP1里面的内容拷贝到APP2
* @输入参数:    无
* @输出参数:    无
* @返回值:      无
*/
void CopyToApp2(void)
{
    //第一步，计算APP1长度
    uint32_t app1ByteLen = 0;
    uint32_t i = 0;
    uint32_t address = FLASH_APP2_ADDRESS - 4;
    UpdateRunFlag WriteRunFlag;
    for(;address > FLASH_APP1_ADDRESS;address -= 4)
    {
        __I uint32_t *data = (__I uint32_t*)address;
        if(*data != 0xFFFFFFFF)
            break;
    }
    if(address <= FLASH_APP1_ADDRESS)
        return;
    app1ByteLen = address - FLASH_APP1_ADDRESS + 4;
    
    //第二步，擦除APP2
    EraseApp2();
    
    //第三步，拷贝
    for(i = 0;i < app1ByteLen;i+=4)
    {
        __I uint32_t *data = (__I uint32_t*)(FLASH_APP1_ADDRESS + i);
        FLASH_ProgramWord(FLASH_APP2_ADDRESS + i,*data);
    }
    
    //第四步，重新计算CRC和长度
    GetRunFlag(&WriteRunFlag);
    //WriteRunFlag.App2CRC = CRC16((uint8_t*)FLASH_APP2_ADDRESS,app1ByteLen);
    WriteRunFlag.App2Size = app1ByteLen;
    SetRunFlag(&WriteRunFlag);
}

/**
* @函数名:      CopyToApp1
* @功能描述:    把APP2里面的内容拷贝到APP1
* @输入参数:    无
* @输出参数:    无
* @返回值:      无
*/
void CopyToApp1(void)
{
    //第一步，计算APP2长度
    uint32_t app2ByteLen = 0;
    uint32_t i = 0;
    uint32_t address = FLASH_APP2_ADDRESS + APP_MAX_SIZE - 4;
    UpdateRunFlag WriteRunFlag;
    for(;address > FLASH_APP2_ADDRESS;address -= 4)
    {
        __I uint32_t *data = (__I uint32_t*)address;
        if(*data != 0xFFFFFFFF)
            break;
    }
    if(address <= FLASH_APP2_ADDRESS)
        return;
    app2ByteLen = address - FLASH_APP2_ADDRESS + 4;
    
    //第二步，擦除APP1
    EraseApp1();
    
    //第三步，拷贝
    for(i = 0;i < app2ByteLen;i+=4)
    {
        __I uint32_t *data = (__I uint32_t*)(FLASH_APP2_ADDRESS + i);
        FLASH_ProgramWord(FLASH_APP1_ADDRESS + i,*data);
    }
    
    //第四步，重新计算CRC和长度
    GetRunFlag(&WriteRunFlag);
    //WriteRunFlag.App1CRC = CRC16((uint8_t*)FLASH_APP1_ADDRESS,app2ByteLen);
    WriteRunFlag.App1Size = app2ByteLen;
    SetRunFlag(&WriteRunFlag);
}

/**
* @函数名:      App1Vaild
* @功能描述:    App1程序是否有效
* @输入参数:    无
* @输出参数:    无
* @返回值:      0:无效,其他:有效
*/
int App1Vaild(void)
{
    __I UpdateRunFlag* RunFlag;
    uint8_t i = 0;
    uint8_t sha256[32];
    //查看栈顶是否合法
    if (((*(__IO uint32_t*)FLASH_APP1_ADDRESS) & 0x2FFE0000 ) != 0x20000000)
    { 
        return 0;
    }
    
    //查看CRC是否正确
    RunFlag = (__I UpdateRunFlag* )(FLASH_UPDATE_FLAG_ADDRESS);
    //如果全是是0xFFFF，说明没有经过任何升级
    for(i = 0;i < 32;i++)
    {
        if(RunFlag->App1SHA256[i] != 0xFF)
        {
            break;
        }
    }
    if(i == 32)
        return 1;
    else
    {
        if(RunFlag->App1Size < APP_MAX_SIZE)
        {
            //计算SHA256
            core_sha256((uint8_t*)FLASH_APP1_ADDRESS,RunFlag->App1Size,sha256);
            if(memcmp(&RunFlag->App1SHA256,sha256,32) == 0)
            {
                return 1;
            }
        }
    }

    return 0;
}

/**
* @函数名:      App2Vaild
* @功能描述:    App2程序是否有效
* @输入参数:    无
* @输出参数:    无
* @返回值:      0:无效,其他:有效
*/
int App2Vaild(void)
{
    __I UpdateRunFlag* RunFlag;
    uint8_t sha256[32];
    //查看栈顶是否合法
    if (((*(__IO uint32_t*)FLASH_APP2_ADDRESS) & 0x2FFE0000 ) != 0x20000000)
    { 
        return 0;
    }

    //查看CRC是否正确
    if(RunFlag->App2Size < APP_MAX_SIZE)
    {
        //计算SHA256
        core_sha256((uint8_t*)FLASH_APP2_ADDRESS,RunFlag->App2Size,sha256);
        if(memcmp(&RunFlag->App2SHA256,sha256,32) == 0)
        {
            return 1;
        }
    }
    

    return 1;
}

/**
* @函数名:      GetRunFlag
* @功能描述:    获取运行标志
* @输入参数:    运行标志
* @输出参数:    无
* @返回值:      无
*/
void GetRunFlag(UpdateRunFlag* flg)
{
    const UpdateRunFlag* RunFlag;
    RunFlag = (UpdateRunFlag* )(FLASH_UPDATE_FLAG_ADDRESS);
    memcpy(flg,RunFlag,sizeof(UpdateRunFlag));
}

/**
* @函数名:      SetRunFlag
* @功能描述:    获取运行标志
* @输入参数:    运行标志
* @输出参数:    无
* @返回值:      无
*/
void SetRunFlag(UpdateRunFlag* flg)
{
    FLASH_ErasePage(FLASH_UPDATE_FLAG_ADDRESS);
    ProgramFlash(FLASH_UPDATE_FLAG_ADDRESS,(uint8_t*)flg,sizeof(UpdateRunFlag));
}

/**
* @函数名:      SetUpdateRunFlag
* @功能描述:    设置运行标志位
* @输入参数:    运行标志位
* @输出参数:    无
* @返回值:      无
*/
void SetUpdateRunFlag(uint32_t flg)
{
    UpdateRunFlag runParam;
    GetRunFlag(&runParam);
    if(runParam.UpdateHandle != flg)
    {
        runParam.UpdateHandle = flg;
        SetRunFlag(&runParam);
    }
}

/**
* @函数名:      JumpApp1
* @功能描述:    跳转到APP1
* @输入参数:    无
* @输出参数:    无
* @返回值:      无
*/
typedef  void (*pFunction)(void);
pFunction Jump_To_Application;
void JumpApp1()
{
    uint32_t JumpAddress = 0;
    /* Jump to user application */
    JumpAddress = *(__IO uint32_t*) (FLASH_APP1_ADDRESS + 4);
    Jump_To_Application = (pFunction) JumpAddress;
    /* Initialize user application's Stack Pointer */
    __set_MSP(*(__IO uint32_t*) FLASH_APP1_ADDRESS);
    Jump_To_Application();
}

/**
* @函数名:      Reboot
* @功能描述:    重启
* @输入参数:    无
* @输出参数:    无
* @返回值:      无
*/
void Reboot(void)
{
    NVIC_SystemReset();
}
